package com.fangcang.mapping.service.impl;

import com.alibaba.fastjson.JSON;
import com.fangcang.common.IncrementDTO;
import com.fangcang.common.IncrementType;
import com.fangcang.common.PaginationSupportDTO;
import com.fangcang.common.ResponseDTO;
import com.fangcang.common.config.OtaConfig;
import com.fangcang.common.constant.Constant;
import com.fangcang.common.constant.InitData;
import com.fangcang.common.enums.ChannelTypeEnum;
import com.fangcang.common.enums.ErrorCodeEnum;
import com.fangcang.common.util.DateUtil;
import com.fangcang.common.util.HttpClientUtil;
import com.fangcang.common.util.PropertyCopyUtil;
import com.fangcang.common.util.StringUtil;
import com.fangcang.mapping.domain.CtriposShopInfoDO;
import com.fangcang.mapping.dto.ctripos.CtriposMapHotelDTO;
import com.fangcang.mapping.dto.ctripos.CtriposMapRateplanDTO;
import com.fangcang.mapping.dto.ctripos.CtriposMapRoomtypeDTO;
import com.fangcang.mapping.dto.ctripos.CtriposShopInfoDTO;
import com.fangcang.mapping.mapper.CtriposCommonMapper;
import com.fangcang.mapping.mapper.CtriposMapHotelMapper;
import com.fangcang.mapping.mapper.CtriposMapRateplanMapper;
import com.fangcang.mapping.mapper.CtriposMapRoomtypeMapper;
import com.fangcang.mapping.mapper.CtriposShopInfoMapper;
import com.fangcang.mapping.request.ctripos.BatchSyncCtripRequestCtripos;
import com.fangcang.mapping.request.ctripos.CtriposBaseRequest;
import com.fangcang.mapping.request.ctripos.PushConfirmNoRequest;
import com.fangcang.mapping.request.ctripos.PushPriceAndQuotaToCtripRequest;
import com.fangcang.mapping.request.ctripos.QueryHotelListRequest;
import com.fangcang.mapping.request.ctripos.QueryRoomOrRateListRequest;
import com.fangcang.mapping.request.ctripos.SyncCtripCommodityRequest;
import com.fangcang.mapping.request.ctripos.SyncCtripHotelRequest;
import com.fangcang.mapping.request.ctripos.SyncCtripRoomTypeRequest;
import com.fangcang.mapping.service.CtriposMappingService;
import com.fangcang.product.mapper.PricePlanMapper;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @description:
 * @author: zhanwang
 * @create: 2019-03-11 18:06
 **/
@Service
@Slf4j
public class CtriposMappingServiceImpl implements CtriposMappingService {

    @Autowired
    private CtriposCommonMapper ctriposCommonMapper;

    @Autowired
    private CtriposShopInfoMapper ctriposShopInfoMapper;

    @Autowired
    private CtriposMapHotelMapper ctriposMapHotelMapper;

    @Autowired
    private CtriposMapRoomtypeMapper ctriposMapRoomtypeMapper;

    @Autowired
    private CtriposMapRateplanMapper ctriposMapRateplanMapper;

    @Autowired
    private OtaConfig otaConfig;

    @Autowired
    private PricePlanMapper pricePlanMapper;

    @Override
    public ResponseDTO<List<CtriposShopInfoDTO>> queryShopList(String merchantCode) {
        ResponseDTO<List<CtriposShopInfoDTO>> responseDTO = ResponseDTO.successInstance();
        List<CtriposShopInfoDTO> ctriposShopInfoDTOS = InitData.merchantCtriposShopMap.get(merchantCode);
        if (CollectionUtils.isEmpty(ctriposShopInfoDTOS)) {
            //查询数据库，并放入内存中
            CtriposShopInfoDO ctriposShopInfoDO = new CtriposShopInfoDO();
            ctriposShopInfoDO.setMerchantCode(merchantCode);
            List<CtriposShopInfoDO> ctriposShopInfoDOS = ctriposShopInfoMapper.select(ctriposShopInfoDO);

            if (CollectionUtils.isEmpty(ctriposShopInfoDOS)) {
                log.error("{}没有配置店铺信息。", merchantCode);
                responseDTO.setErrorCode(ErrorCodeEnum.MAPPING_SHOP_NOT_FOUND);
                responseDTO.setResult(Constant.NO);
            } else {
                ctriposShopInfoDTOS = PropertyCopyUtil.transferArray(ctriposShopInfoDOS, CtriposShopInfoDTO.class);
                InitData.merchantCtriposShopMap.put(merchantCode, ctriposShopInfoDTOS);
            }
        }
        responseDTO.setModel(ctriposShopInfoDTOS);
        return responseDTO;
    }

    @Override
    public void initCtriposShopInfo() {
        //对应InitData.merchantCtriposShopMap
        Map<String, List<CtriposShopInfoDTO>> shopInfoMap = new HashMap<>();

        //对应InitData.merchantCtriposShopChanneCodeMap
        Map<String, String> shopChannelMap = new HashMap<>();

        //对应InitData.merchantCtriposChannelCodeShopMap
        Map<String, String> merchantChannelCodeShopMap = new HashMap<>();

        List<CtriposShopInfoDO> shopInfoList = ctriposShopInfoMapper.selectAll();
        for (CtriposShopInfoDO shopInfo : shopInfoList) {
            CtriposShopInfoDTO shopInfoDTO = PropertyCopyUtil.transfer(shopInfo, CtriposShopInfoDTO.class);
            if (shopInfoMap.containsKey(shopInfo.getMerchantCode())) {
                shopInfoMap.get(shopInfo.getMerchantCode()).add(shopInfoDTO);
            } else {
                List<CtriposShopInfoDTO> shopInfoList1 = new ArrayList<>();
                shopInfoList1.add(shopInfoDTO);
                shopInfoMap.put(shopInfoDTO.getMerchantCode(), shopInfoList1);
            }

            shopChannelMap.put(shopInfo.getMerchantCode() + "-" + shopInfoDTO.getId(), ChannelTypeEnum.CTRIPOS.key);
            merchantChannelCodeShopMap.put(shopInfo.getMerchantCode() + "-" + ChannelTypeEnum.CTRIPOS.key, shopInfo.getId().toString());
        }

        InitData.merchantCtriposShopMap = shopInfoMap;
        InitData.merchantShopChanneCodeMap = shopChannelMap;
        InitData.merchantChannelCodeShopMap = merchantChannelCodeShopMap;
    }

    @Override
    public ResponseDTO<PaginationSupportDTO<CtriposMapHotelDTO>> queryHotelList(QueryHotelListRequest request) {
        ResponseDTO responseDTO = new ResponseDTO(ErrorCodeEnum.SUCCESS);
        if (StringUtils.isEmpty(request.getMerchantCode()) || request.getShopId() == null) {
            responseDTO.setErrorCode(ErrorCodeEnum.INVALID_INPUTPARAM);
            return responseDTO;
        }

        //分页查询
        PageHelper.startPage(request.getCurrentPage(), request.getPageSize());
        List<CtriposMapHotelDTO> hotelDTOS = ctriposCommonMapper.queryHotelList(request);
        PageInfo<CtriposMapHotelDTO> pageInfo = new PageInfo<>(hotelDTOS);
        PaginationSupportDTO paginationSupportDTO = new PaginationSupportDTO();
        paginationSupportDTO.setTotalCount(pageInfo.getTotal());
        paginationSupportDTO.setTotalPage(pageInfo.getPages());
        paginationSupportDTO.setCurrentPage(pageInfo.getPageNum());
        paginationSupportDTO.setPageSize(pageInfo.getPageSize());
        paginationSupportDTO.setItemList(hotelDTOS);

        responseDTO.setModel(paginationSupportDTO);
        return responseDTO;
    }

    @Override
    public ResponseDTO<CtriposMapHotelDTO> queryRoomOrRateList(QueryRoomOrRateListRequest request) {
        ResponseDTO<CtriposMapHotelDTO> responseDTO = ResponseDTO.successInstance();
        if (StringUtils.isEmpty(request.getMerchantCode()) || request.getShopId() == null
                || request.getHotelId() == null) {
            responseDTO.setErrorCode(ErrorCodeEnum.INVALID_INPUTPARAM);
            return responseDTO;
        }

        List<CtriposMapHotelDTO> ctriposMapHotelDTOS = ctriposCommonMapper.queryRoomOrRateList(request);
        if (CollectionUtils.isNotEmpty(ctriposMapHotelDTOS)) {
            responseDTO.setModel(ctriposMapHotelDTOS.get(0));
        }
        return responseDTO;
    }

    @Override
    public ResponseDTO batchPushHotelInfoToCtrip(BatchSyncCtripRequestCtripos request) {
        if (null == request || !StringUtil.isValidString(request.getMerchantCode()) || !StringUtil.isValidString(request.getShopId())
                || CollectionUtils.isEmpty(request.getHotelVOs())) {
            log.error("batchPushHotelInfoToCtrip parameter has null");
            return new ResponseDTO(Constant.NO, ErrorCodeEnum.INVALID_INPUTPARAM.errorCode, "参数异常");
        }

        /**
         *   当推送只有酒店时，需自动补齐房型、价格计划信息
         */
        List<Integer> hotelIds = new ArrayList<>();
        for (SyncCtripHotelRequest hotelVO : request.getHotelVOs()) {
            if (CollectionUtils.isEmpty(hotelVO.getRoomTypeVOs())) {
                hotelIds.add(hotelVO.getHotelId().intValue());
            }
        }

        if (CollectionUtils.isNotEmpty(hotelIds)) {
            // 查房型、价格计划
            QueryRoomOrRateListRequest roomOrRateListRequest = new QueryRoomOrRateListRequest();
            roomOrRateListRequest.setHotelIdList(hotelIds);
            List<CtriposMapHotelDTO> ctriposMapHotelDTOS = ctriposCommonMapper.queryHotelInfoByIds(roomOrRateListRequest);
            Map<Integer, CtriposMapHotelDTO> hotelMap = new HashMap<>();
            for (CtriposMapHotelDTO hotelDTO : ctriposMapHotelDTOS) {
                hotelMap.put(hotelDTO.getHotelId(), hotelDTO);
            }

            // 补齐房型、价格计划
            for (SyncCtripHotelRequest hotelVO : request.getHotelVOs()) {

                CtriposMapHotelDTO hotelDTO = hotelMap.get(hotelVO.getHotelId().intValue());
                if (hotelDTO == null || CollectionUtils.isEmpty(hotelDTO.getRoomtypeDTOList())) {
                    continue;
                }
                List<SyncCtripRoomTypeRequest> roomTypeVOs = new ArrayList<>();
                for (CtriposMapRoomtypeDTO roomtypeDTO : hotelDTO.getRoomtypeDTOList()) {
                    SyncCtripRoomTypeRequest roomTypeVO = new SyncCtripRoomTypeRequest();
                    roomTypeVO.setRoomTypeId(roomtypeDTO.getRoomTypeId().longValue());
                    roomTypeVO.setRoomTypeName(roomtypeDTO.getRoomName());
                    roomTypeVOs.add(roomTypeVO);

                    if (CollectionUtils.isEmpty(roomtypeDTO.getRateplanDTOList())) {
                        continue;
                    }
                    List<SyncCtripCommodityRequest> commodityVOs = new ArrayList<>();
                    for (CtriposMapRateplanDTO rateplanDTO : roomtypeDTO.getRateplanDTOList()) {
                        SyncCtripCommodityRequest commodityVO = new SyncCtripCommodityRequest();
                        commodityVO.setCommodityId(rateplanDTO.getCommodityId().longValue());
                        commodityVO.setCommodityName(rateplanDTO.getCommodityName());
                        commodityVOs.add(commodityVO);
                    }
                    roomTypeVO.setCommodityVOs(commodityVOs);
                }

                hotelVO.setRoomTypeVOs(roomTypeVOs);
            }
        }


        ResponseDTO responseDTO = invokeCtriposServer(request, otaConfig.getPushCtriposHotel());
        log.info("调用ctripos-server的推送酒店接口.请求：{}，返回：{}", JSON.toJSONString(request), JSON.toJSONString(responseDTO));
        return responseDTO;
    }

    @Override
    public ResponseDTO batchSyncHotelInfoFromCtrip(BatchSyncCtripRequestCtripos request) {
        if (null == request || !StringUtil.isValidString(request.getMerchantCode()) || !StringUtil.isValidString(request.getShopId())
                || CollectionUtils.isEmpty(request.getHotelVOs())) {
            log.error("batchPushHotelInfoToCtrip parameter has null");
            return new ResponseDTO(Constant.NO, ErrorCodeEnum.INVALID_INPUTPARAM.errorCode, "参数异常");
        }

        ResponseDTO responseDTO = invokeCtriposServer(request, otaConfig.getSyncCtriposHotel());
        log.info("调用ctripos-server的推送酒店接口.请求：{}，返回：{}", JSON.toJSONString(request), JSON.toJSONString(responseDTO));
        return responseDTO;
    }

    @Override
    public ResponseDTO pushPriceAndQuotaToCtrip(PushPriceAndQuotaToCtripRequest request) {
        if (null == request || !StringUtil.isValidString(request.getMerchantCode()) || !StringUtil.isValidString(request.getShopId())
                || request.getPricePlanId() == null) {
            log.error("batchPushHotelInfoToCtrip parameter has null");
            return new ResponseDTO(Constant.NO, ErrorCodeEnum.INVALID_INPUTPARAM.errorCode, "参数异常");
        }

        //转换成增量一样的参数，调用增量的方法
        List<IncrementDTO> incrementDTOList = new ArrayList<>();
        IncrementDTO incrementDTO = new IncrementDTO();
        incrementDTO.setStartDate(DateUtil.dateToString(DateUtil.getCurrentDate()));
        //默认推送30天的数据过去
        incrementDTO.setEndDate(DateUtil.dateToString(DateUtil.getDate(DateUtil.getCurrentDate(), 30, 0)));
        incrementDTO.setMRatePlanId(request.getPricePlanId());
        incrementDTO.setMRoomTypeId(request.getRoomTypeId());
        incrementDTO.setMHotelId(request.getHotelId());
        incrementDTOList.add(incrementDTO);
        request.setIncrementDTOList(incrementDTOList);
        request.setIncrementType(IncrementType.instant().andRoomStatus().andPrice());

        ResponseDTO responseDTO = invokeCtriposServer(request, otaConfig.getPriceAndRoomStatus());
        log.info("调用ctripos-server的同步价格和房态接口.请求：{}，返回：{}", JSON.toJSONString(request), JSON.toJSONString(responseDTO));
        return responseDTO;
    }

    @Override
    public ResponseDTO pushConfirmNoToOta(PushConfirmNoRequest request) {
        if (null == request || !StringUtil.isValidString(request.getMerchantCode())
                || !StringUtil.isValidString(request.getConfirmNo())
                || !StringUtil.isValidString(request.getOrderCode())) {
            log.error("pushConfirmNoToOta parameter has null");
            return new ResponseDTO(Constant.NO, ErrorCodeEnum.INVALID_INPUTPARAM.errorCode, "参数异常");
        }
        ResponseDTO responseDTO = new ResponseDTO();
        try {
            String shopId = InitData.merchantChannelCodeShopMap.get(request.getMerchantCode() + "-" + request.getChannelCode());
            request.setShopId(shopId);
            responseDTO = invokeCtriposServer(request, otaConfig.getPushConfirmNo());
            log.info("调用ctripos-server的推送确认号接口.请求：{}，返回：{}", JSON.toJSONString(request), JSON.toJSONString(responseDTO));
        } catch (Exception e) {
            log.error("pushConfirmNoToOta error: ", e);
            responseDTO.setErrorCode(ErrorCodeEnum.SYSTEM_EXCEPTION);
        }

        return responseDTO;
    }

    private String getRemoteUrl(String channelCode, String methodName) {
        //根据渠道获取对应的应用，再拼接上对应的方法名称
        return otaConfig.getUrlMap().get(channelCode) + methodName;
    }

    private String getChannelCode(CtriposBaseRequest ctriposBaseRequest) {
        String merchantShopKey = ctriposBaseRequest.getMerchantCode() + "-" + ctriposBaseRequest.getShopId();
        return InitData.merchantShopChanneCodeMap.get(merchantShopKey);
    }


    private ResponseDTO invokeCtriposServer(CtriposBaseRequest ctriposBaseRequest, String methodName) {
        ResponseDTO responseDTO = new ResponseDTO(ErrorCodeEnum.SUCCESS);
        //调用ctripos
        String channelCode = getChannelCode(ctriposBaseRequest);
        String url = getRemoteUrl(channelCode, methodName);
        String requestParam = JSON.toJSONString(ctriposBaseRequest);
        String responseStr = null;
        try {
            responseStr = HttpClientUtil.postJson(url, requestParam);
        } catch (IOException e) {
            log.error("调用ctripos-server时IO异常。URL:{}。请求参数：{}。", url, requestParam, e);
            responseDTO.setErrorCode(ErrorCodeEnum.SYSTEM_EXCEPTION);
            return responseDTO;
        }
        responseDTO = JSON.parseObject(responseStr, ResponseDTO.class);

        return responseDTO;
    }
}
